/***************************************************************************\
 * The Mattise Kernel														*
 * Copyright 2007 Matthew Iselin											*
 * Licensed under the GPL													*
 *																			*
 * mouse.cc																	*
 *																			*
 * Handles PS2 mouse input.													*
 *																			*
\***************************************************************************/

#include <system.h>
#include <iostream>
#include <mouse.h>

// commands for PS2
#define ENABLE_PS2			0xA8
#define KBD_STAT			0x64
#define MOUSE				0xD4
#define MOUSE_STREAM		0xF4
#define MOUSE_DISABLE		0xF5
#define KBD_CMD				0x60
#define DISABLE_KBD			0xAD
#define ENABLE_KBD			0xAE
#define SET_DEFAULTSETTINGS	0xF6
#define MOUSE_ACK			0xFA

// wait for the mouse - this is in the OSFAQ (http://www.osdev.org/osfaq2)
void MouseWait( uchar_t type )
{
	// timeout
/*	uint_t timeout = 500000;
	uchar_t tmp;
	
	// data?
	if( type == 0 )
	{
		// keep going until the timeout finishes or data comes
		while( timeout-- )
		{
			// check for data
			tmp = inportb( 0x64 );
			
			// check if the buffer is full
			if( ( tmp & 1 ) == 1 )
			{
//				if( ( tmp & 32 ) == 1 )
					return;
			}
		}
	}
	else // signal
	{
		// keep going until the timeout finishes or data comes
		while( timeout-- )
		{
			// check for data
			if( ( inportb( 0x64 ) & 2 ) == 0 )
				return;
		}
	}*/
}

// writes a byte to the mouse
void mouseWrite( uchar_t b )
{
	// wait
	MouseWait( 1 );
	
	// send the command (ask for the mouse)
	outportb( 0x64, 0xD4 );
	
	// wait
	MouseWait( 1 );
	
	// send the data
	outportb( 0x60, b );
}

// gets a byte from the mouse
uchar_t mouseRead()
{
	// wait
	MouseWait( 0 );
	
	// read
	return inportb( 0x60 );
}

// first stage of initialization - enable the aux device
void EnableAuxDevice()
{
	// wait first
	MouseWait( 1 );
	
	// enable the auxillary device
//	cout << "[MOUS] Enabling auxillary device on PS2... ";
	outportb( 0x64, 0xA8 );
//	cout << "Done!" << endl;
}

// enables the mouse interrupt - does not install a handler!
void EnableMouseIRQ()
{
	// always wait
//	MouseWait( 1 );
	
	// enable, by reading in the current data then writing it again
//	cout << "[MOUS] Enabling IRQ... ";
	outportb( 0x64, 0x20 );
	MouseWait( 0 );
	uchar_t status = inportb( 0x60 ) | 2;
	MouseWait( 1 );
	outportb( 0x64, 0x60 );
	MouseWait( 1 );
	outportb( 0x60, status );
//	cout << "Done!" << endl;
}

// set the default settings
void SetDefaultSettings()
{
	// send the command
	mouseWrite( SET_DEFAULTSETTINGS );
	
	// get the acknowledgement
	uchar_t ack = mouseRead();
	
	// did it ack?
	if( ack != MOUSE_ACK )
	{
		// something bad happened!
//		cout << "[MOUS] Mouse didn't ack SET_DEFAULTSETTINGS" << endl;
	}
}

// enable the mouse
void MouseEnable()
{
	// write the enable command
	mouseWrite( 0xF4 );
	
	// get the acknowledgement
	uchar_t ack = mouseRead();
	
	// did it ack?
	if( ack != MOUSE_ACK )
	{
		// something bad happened!
//		cout << "[MOUS] Mouse didn't ack ENABLE" << endl;
	}
}

// movement indicators
uchar_t status,xmove,ymove,readable = 0,IrqIndex=0;
uchar_t bytes[4];

// mouse IRQ handler
void MouseIRQ( struct regs* r )
{
	// the data from the port
	uchar_t dat = inportb( 0x64 );
	
	// check if data is available
	if( ( dat & 0x01 ) != 0 && ( dat & 32 ) != 0 )
	{
		// read a byte
		bytes[IrqIndex++]= inportb( 0x60 );
	}
	
	// check...
	if( IrqIndex == 3 )
	{
		readable = 1;
		IrqIndex = 0;
	}
}

// gets and returns the mouse data
// note: all data seems to come in back-to-front. don't ask me why
void GetMouseData( MOUSEDATA* data )
{
	if( readable )
	{
		data->xmove = bytes[0];
		data->ymove = bytes[1];
		data->MouseStatus.status = bytes[2];
		readable = 0;
	}
	else
	{
		data->xmove = 0;
		data->ymove = 0;
	}
}

// installs the mouse IRQ handler
void InstallMouseIRQ()
{
	// IRQ12 is the mouse
	irq_install_handler( 12, MouseIRQ );
}

// initializes the mouse
void mouseInit()
{
	// enable the device
	EnableAuxDevice();
	
	// install the IRQ
	InstallMouseIRQ();
	
	// enable the IRQ
	EnableMouseIRQ();
	
	// set the defaults
	SetDefaultSettings();
	
	// enable the mouse, finally
	MouseEnable();
}

/*
// check the status port
void CheckPort()
{
	uchar_t tmp;
	while( true )
	{
		tmp = inportb( KBD_STAT );
		if( ( tmp & 2 ) == 0 )
			break;
	}
}

// checks the mouse
char CheckMouse()
{
	uchar_t temp;
	temp = inportb( KBD_STAT );

	if( temp & 1 )
		return 0;
	else
		return 1;
}

// waits for the mouse
void WaitMouse()
{
	outportb( KBD_STAT, MOUSE );
	CheckPort();
}

// set the ps2 bytes
void enableMouse()
{
	// send 
	// mouse enabled
	outportb( KBD_STAT, ENABLE_PS2 );
	CheckPort();
}

// sets the streaming mode
void StartStream()
{
	WaitMouse();
	outportb( KBD_CMD, MOUSE_STREAM );
	CheckPort();
	CheckMouse();
}


// get a byte from the mouse
char GetByte()
{
	//while( CheckMouse() );
	MouseWait(0);
	char ret = inportb( 0x60 );

	return ret;
}

// is there data to read?
int readable = 0;
int i = 0;

// data pointer
MOUSEDATA LocalData;

// irq handler for the mouse
void MouseIRQ( struct regs* r )
{
	LocalData.status = GetByte();
	LocalData.xcoord = GetByte();
	LocalData.ycoord = GetByte();
	
	readable = 1;
	
	cout << "IRQ(" << LocalData.xcoord << "," << LocalData.ycoord << ") ";
}

// installs the IRQ
void InstallMouseIRQ()
{
	// enable the irq
	outportb( 0x64, 0x20 );
	CheckPort();
	unsigned char c = inportb( 0x60 ) | 2;
	outportb( 0x64, 0x60 );
	CheckPort();
	outportb( 0x60, c );

	// install the irq handler
	irq_install_handler( 12, MouseIRQ );
}

// get data from the mouse
void GetMouseData( MOUSEDATA* data )
{
	// is there data?
	if( ! readable )
	{
		// there's no data yet, so just make no movement
		data->status = 0;
		data->xcoord = 0;
		data->ycoord = 0;
		
		// go back to caller
		return;
	}
	
	// disable interrupts
	//__asm__ __volatile__ ( "cli" );

	// get the important stuff
	LocalData.leftbut = LocalData.status & 1;
	LocalData.rightbut = ( LocalData.status & 2 ) >> 1;
	LocalData.midbut = ( LocalData.status & 4 ) >> 2;
	LocalData.xsign = ( LocalData.status & 16 ) >> 4;
	LocalData.ysign = ( LocalData.status & 32 ) >> 5;
	LocalData.xover = ( LocalData.status & 64 ) >> 6;
	LocalData.yover = ( LocalData.status & 128 ) >> 7;

	// return the data
	*data = LocalData;

	// readable is false now
	readable = 0;
	
	// enable interrupts
	//__asm__ __volatile__ ( "sti" );
}

// initializes the mouse
void mouseInit()
{
	// install the IRQ
	InstallMouseIRQ();
	
	// enable the mouse
	enableMouse();
	
	// start streaming
	StartStream();
}*/
